/**
 * @(#)LocationHelper.java        1.0 4/11/12
 * 
 * Copyright (c) 2012 public static droid main
 * Georgia Institute of Technology, Atlanta, GA
 * All rights reserved.
 */
package edu.gatech.oad.publicstaticdroidmain.taskmanager.model;

import java.io.IOException;
import java.io.InputStream;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONArray;

import com.google.android.maps.GeoPoint;

/**
 * Contains static methods that assist in dealing with locations, such 
 * as methods to determine if a latitude/longitude is valid, and methods
 * to translate an address into a latitude and longitude and vice
 * versa.
 * 
 * @version 1.0 3/25/12
 * @author Raheel Merali
 *
 */
public class LocationHelper {

    private static final double MIN_LATITUDE = -90;
    private static final double MAX_LATITUDE = 90;

    private static final double MIN_LONGITUDE = -180;
    private static final double MAX_LONGITUDE = 180;

    private LocationHelper() { }

    /**
     * Determines if the specified number is a valid latitude value
     * (between -90 and 90).
     * 
     * @param latitude the latitude
     * @return true if the latitude is valid; false otherwise
     */
    public static boolean isValidLatitude(double latitude) {
	return (latitude >= MIN_LATITUDE && latitude <= MAX_LATITUDE);
    }

    /**
     * Determines if the specified number is a valid longitude value
     * (between -180 and 180).
     * 
     * @param longitude the longitude
     * @return true if the longitude is valid; false otherwise
     */
    public static boolean isValidLongitude(double longitude) {
	return (longitude >= MIN_LONGITUDE && longitude <= MAX_LONGITUDE);
    }

    /**
     * Returns the address that corresponds to the specified latitude
     * and longitude.  This method uses the Google Maps web API.
     * 
     * @param latitude the latitude
     * @param longitude the longitude
     * @return the address that corresponds to the specified coordinates,
     * 		null if no address corresponds to the specified
     * 		coordinates
     */
    public static String getAddressFromCoordinates(double latitude, double longitude) {
	String address = null;

	JSONObject requestResults = executeJsonHttpRequest("https://maps.google.com/maps/api/geocode/json?latlng=" + latitude + "," + longitude + "&sensor=false");
	if (requestResults != null && requestSuccessful(requestResults)) {
	    try {
		address = ((JSONArray)requestResults.get("results")).getJSONObject(0).getString("formatted_address");
	    } catch (JSONException e) {
		e.printStackTrace();
	    }
	}

	return address;
    }

    /**
     * Returns a GeoPoint containing the latitude and longitude values that
     * correspond to the specified address.  This method uses the Google
     * Maps web API.
     * 
     * @param address the address
     * @return a GeoPoint containing the latitude and longitude values that
     * 		correspond to the specified address; null if the specified
     * 		address does not exist
     */
    public static GeoPoint getCoordinatesFromAddress(String address) {
	GeoPoint point = null;

	JSONObject requestResults = executeJsonHttpRequest("https://maps.google.com/maps/api/geocode/json?address=" + address + "&sensor=false");
	if (requestResults != null && requestSuccessful(requestResults)) {
	    try {
		double lon = ((JSONArray)requestResults.get("results")).getJSONObject(0)
			.getJSONObject("geometry").getJSONObject("location")
			.getDouble("lng");

		double lat = ((JSONArray)requestResults.get("results")).getJSONObject(0)
			.getJSONObject("geometry").getJSONObject("location")
			.getDouble("lat");

		point = new GeoPoint((int) (lat * 1E6), (int) (lon * 1E6));
	    } catch (JSONException e) {
		e.printStackTrace();
	    }
	}

	return point;
    }

    /**
     * Sends a JSON http request to the following URL and retrieves
     * the resulting JSON object.
     * 
     * @param url the request URL
     * @return a JSON object containing the results of the request
     */
    private static JSONObject executeJsonHttpRequest(String url) {
	if (url == null) {
	    throw new NullPointerException("URL cannot be null");
	}

	StringBuilder stringBuilder = new StringBuilder();

	try {
	    url = url.replaceAll(" ","%20");    

	    HttpPost httppost = new HttpPost(url);
	    HttpClient client = new DefaultHttpClient();
	    HttpResponse response = client.execute(httppost);
	    HttpEntity entity = response.getEntity();
	    InputStream stream = entity.getContent();

	    int nextByte;
	    while ((nextByte = stream.read()) != -1) {
		stringBuilder.append((char) nextByte);
	    }

	} catch (ClientProtocolException e) {
	    e.printStackTrace();
	} catch (IOException e) {
	    e.printStackTrace();
	}

	JSONObject jsonObject = new JSONObject();

	try {
	    jsonObject = new JSONObject(stringBuilder.toString());
	} catch (JSONException e) {
	    e.printStackTrace();
	}

	return jsonObject;
    }

    private static boolean requestSuccessful(JSONObject jsonObject) {
	boolean successful = false;

	try {
	    if (jsonObject.getString("status").equals("OK")) {
		successful = true;
	    }
	} catch (JSONException jse) {
	    jse.printStackTrace();
	}

	return successful;
    }
}
